//事件常量
const MOUSE_MOVE = Cesium.ScreenSpaceEventType.MOUSE_MOVE //鼠标移动事件
const RIGHT_MOUSE_CLICK = Cesium.ScreenSpaceEventType.RIGHT_CLICK //鼠标右键点击事件
const LEFT_MOUSE_CLICK = Cesium.ScreenSpaceEventType.LEFT_CLICK //鼠标左键点击事件
const LEFT_MOUSE_DOWN = Cesium.ScreenSpaceEventType.LEFT_DOWN //鼠标左键按下事件
const LEFT_MOUSE_UP = Cesium.ScreenSpaceEventType.LEFT_UP //鼠标左键抬起事件
/**
 * 图形工厂类-绘制Graphic图形
 */
class GraphicFactory {
  //properties
  graphicTypes = ['point', 'label', 'polyline', 'polygon', 'rectangle']
  entityCollector = Object.create(null) //实体收集器集合
  viewer = undefined //viewer对象持有
  leftClick_handler = undefined //鼠标左键单击事件句柄
  rightClick_handler = undefined //鼠标右键单击事件句柄
  edit_handler = undefined //编辑模式事件句柄
  isEditable = false //标识是否为编辑模式
  editableColor = Cesium.Color.ORANGE.withAlpha(0.6) //可编辑模式下的颜色
  selectedEntity = {
    feature: null, //Entity对象
    originColor: null, //Entity对象原始颜色
    type: null, //Entity对象类型
  } //用于记录正在被编辑的·Entity对象
  tempPoints = {
    belong: null, //所属的Entity
    pointSet: new Array(), //通过Entity顶点临时添加的point点位
  } //存放临时Point点位数据+所属的Graphic几何对象

  /**
   * 构造器中不执行任何操作,将句柄实例化延迟到构造
   */
  constructor(viewer) {
    this.viewer = viewer
    //实例化实体收集器
    for (let i = 0; i < this.graphicTypes.length; i++)
      this.entityCollector[this.graphicTypes[i]] = new Cesium.EntityCollection()
  }

  /**
   * 计算Cesium.Rectangle矩形的所有顶点坐标
   * @returns Array<Cartesian3>
   */
  getVertexFromRectangle(rectangle) {
    //获取矩形的范围信息
    const { west = -180, south = -90, east = 180, north = 90 } = rectangle
    //弧度转角度
    const [d_west, d_south, d_east, d_north] = [
      Cesium.Math.toDegrees(west),
      Cesium.Math.toDegrees(south),
      Cesium.Math.toDegrees(east),
      Cesium.Math.toDegrees(north),
    ]
    //创建Cartesian33坐标序列
    return [
      Cesium.Cartesian3.fromDegrees(d_west, d_south),
      Cesium.Cartesian3.fromDegrees(d_east, d_north),
    ]
  }

  /**
   * 根据坐标序列计算Cesium.Rectangle矩形实例
   * @returns Cesium.Rectangle
   */
  getRectangleFromCartesian3Array(cartesian3Array) {
    return Cesium.Rectangle.fromCartesianArray(
      cartesian3Array,
      Cesium.Ellipsoid.WGS84
    )
  }

  //设置编辑模式下,Entity显示的颜色
  setEditableColor(color) {
    if (Cesium.defined(color) && color instanceof Cesium.Color) {
      this.editableColor = color
      return true
    }
    return false
  }

  //判断被选中对象是否有效
  validSelectedEntity() {
    return (
      this.selectedEntity.feature &&
      this.selectedEntity.originColor &&
      this.selectedEntity.type
    )
  }

  //重置被选中对象为空
  resetSelectedEntity() {
    this.selectedEntity = {
      feature: null, //Entity对象
      originColor: null, //Entity对象原始颜色
      type: null, //Entity对象类型
    }
  }

  //从场景中移除并清空临时点
  clearTempPoints() {
    for (let i = 0; i < this.tempPoints.pointSet.length; i++) {
      const point = this.tempPoints.pointSet[i]
      this.viewer.entities.remove(point)
    }
    this.tempPoints.belong = null
    this.tempPoints.pointSet.splice(0, this.tempPoints.pointSet.length)
  }

  //批量添加临时Point
  addBatchTempPoints(list = [], entity, type = 'point') {
    if (!Array.isArray(list) || list.length === 0) return
    this.clearTempPoints() //从场景中移除并清空临时点
    //记录所属Entity
    this.tempPoints.belong = entity
    //添加临时点
    for (let i = 0; i < list.length; i++) {
      const point = this.createPoint({
        color: Cesium.Color.RED,
        pixelSize: 5.0,
        position: new Cesium.CallbackProperty(function () {
          return list[i]
        }, false),
      })
      this.addGraphicToViewer(point, type)
      this.tempPoints.pointSet.push(point)
    }
  }

  //点位检索+更新操作
  updateGraphicVertex() {
    const { belong, pointSet } = this.tempPoints //对象解构
    //判空处理
    if (!Cesium.defined(belong) || pointSet.length === 0) return
    //从pointSet点集中提取Cartesian3世界坐标序列
    const cartesian3Array = pointSet.map((item) => {
      return item.position.getValue()
    })
    //使用新的坐标去更新点位
    switch (belong.name) {
      case 'polygon': {
        const positions = belong.polygon.hierarchy.getValue().positions
        positions.splice(0, positions.length, ...cartesian3Array)
        // = new Cesium.CallbackProperty(function(){
        //     return new Cesium.PolygonHierarchy(cartesian3Array);
        // },false);
        break
      }
      case 'polyline': {
        const positions = belong.polyline.positions.getValue()
        positions.splice(0, positions.length, ...cartesian3Array)
        break
      }
      case 'rectangle': {
        const newRectangle = this.getRectangleFromCartesian3Array(
          cartesian3Array
        )
        //更新坐标序列
        belong.rectangle.coordinates = new Cesium.CallbackProperty(function () {
          return newRectangle
        }, false)
        break
      }
    }
  }

  //切换编辑模式
  switchEditMode(editable = false) {
    if (editable) {
      this.switchToEdit()
    } else {
      this.switchToDraw()
    }
  }

  /**
   * 切换到编辑模式
   */
  switchToEdit() {
    const self = this
    //取消绘图事件
    this.leftClick_handler &&
      this.leftClick_handler.removeInputAction(LEFT_MOUSE_CLICK)
    this.rightClick_handler &&
      this.rightClick_handler.removeInputAction(RIGHT_MOUSE_CLICK)
    this.rightClick_handler &&
      this.rightClick_handler.removeInputAction(MOUSE_MOVE)
    //注册编辑事件
    this.isEditable = true
    //创建编辑事件处理句柄
    this.edit_handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas)

    //左键点击事件-用于获取polyline/polygon/rectangle的顶点
    this.edit_handler.setInputAction(function (event) {
      const { position } = event
      const pickedFeature = self.viewer.scene.pick(position) //Entity对象拾取
      //判断是拾取到entity对象
      if (pickedFeature && pickedFeature.id) {
        const pickedType = pickedFeature.id.name //获取选中对象的类型
        //判断类型
        switch (pickedType) {
          case 'polyline': {
            //polyline->point
            const polylinePositions = pickedFeature.id.polyline.positions.getValue()
            //获取polyline坐标点
            //添加临时point点序列
            self.addBatchTempPoints(polylinePositions, pickedFeature.id)
            break
          }
          case 'polygon': {
            //polygon->point
            const polygonPositions = pickedFeature.id.polygon.hierarchy.getValue()
              .positions
            //添加临时point点序列
            self.addBatchTempPoints(polygonPositions, pickedFeature.id)
            break
          }
          case 'rectangle': {
            //rectangle->point
            const rectangleInstance = pickedFeature.id.rectangle.coordinates.getValue()
            const rectanglePositions = self.getVertexFromRectangle(
              rectangleInstance
            )
            //添加临时Point点位
            self.addBatchTempPoints(rectanglePositions, pickedFeature.id)
            break
          }
        }
      } else {
        console.warn(`未选中任何对象`)
      }
    }, LEFT_MOUSE_CLICK)

    //左键按下事件
    this.edit_handler.setInputAction(function (event) {
      const { position: cartesian2 } = event
      const pickedFeature = self.viewer.scene.pick(cartesian2) //Entity对象拾取
      //判断是拾取到entity对象
      if (pickedFeature && pickedFeature.id) {
        // 禁止地球旋转和缩放，地球的旋转会对鼠标移动监听有影响，所以需要禁止
        self.viewer.scene.screenSpaceCameraController.enableRotate = false
        self.viewer.scene.screenSpaceCameraController.enableZoom = false
        //区分Entity类型
        const pickedType = pickedFeature.id.name
        //判断类型
        switch (pickedType) {
          case 'point': {
            //记录被选中的entity对象
            self.selectedEntity = {
              feature: pickedFeature.id,
              originColor: pickedFeature.id.point.color,
              type: pickedType,
            }
            //修改被选中的Entity颜色
            pickedFeature.id.point.color = self.editableColor
            break
          }
          case 'label': {
            //记录被选中的entity对象
            self.selectedEntity = {
              feature: pickedFeature.id,
              originColor: pickedFeature.id.label.fillColor,
              type: pickedType,
            }
            //修改被选中的Entity颜色
            pickedFeature.id.label.fillColor = self.editableColor
            break
          }
        }
        //左键移动事件
        self.edit_handler.setInputAction(function (movement) {
          //判断被选中对象是否有效
          if (self.validSelectedEntity()) {
            const { endPosition } = movement //获取当前鼠标移动的终点
            const cartesian3_Pos = self.cartesian2ToCartesian3(endPosition) //窗口坐标转换为世界坐标
            //修改被选中对象的位置
            switch (self.selectedEntity.type) {
              case 'point': {
                //点
                self.selectedEntity.feature.position = new Cesium.CallbackProperty(
                  function () {
                    return cartesian3_Pos
                  },
                  false
                )
                break
              }
              case 'label': {
                //标签
                self.selectedEntity.feature.position = new Cesium.CallbackProperty(
                  function () {
                    return cartesian3_Pos
                  },
                  false
                )
                break
              }
            }
            //判断是否需要执行其它图形的顶点更新操作
            const index = self.tempPoints.pointSet.findIndex(
              (item) => item.id === self.selectedEntity.feature.id
            )
            if (index !== -1 && self.tempPoints.belong) {
              //更新临时point集合坐标值
              self.tempPoints.pointSet[
                index
              ].position = new Cesium.CallbackProperty(function () {
                return cartesian3_Pos
              }, false)
              // self.tempPoints.pointSet[index] = self.selectedEntity.feature;
              //其它几何图形的点位更新操作
              self.updateGraphicVertex()
            } else {
              //清空非point/label几何图形的顶点
              self.clearTempPoints() //清空临时顶点数组
            }
          }
        }, MOUSE_MOVE)
        //左键抬起事件
        self.edit_handler.setInputAction(function () {
          //判断被选中对象是否有效
          if (self.validSelectedEntity()) {
            //恢复被选中对象的颜色
            //判断对象的类型
            switch (self.selectedEntity.type) {
              case 'point': {
                self.selectedEntity.feature.point.color =
                  self.selectedEntity.originColor
                break
              }
              case 'label': {
                self.selectedEntity.feature.label.fillColor =
                  self.selectedEntity.originColor
                break
              }
            }
          }
          //重置/置空被选中对象
          self.resetSelectedEntity()
          // 恢复地球旋转和缩放
          self.viewer.scene.screenSpaceCameraController.enableRotate = true
          self.viewer.scene.screenSpaceCameraController.enableZoom = true
        }, LEFT_MOUSE_UP)
      } else {
        console.warn(`未选中任何对象`)
        //点击空白区域时-自动转换到绘图模式
        // self.edit_handler.removeInputAction(LEFT_MOUSE_CLICK);//取消左键单击事件
        // self.edit_handler.removeInputAction(LEFT_MOUSE_DOWN);//取消左键按下事件
        // self.edit_handler.removeInputAction(MOUSE_MOVE);//取消鼠标移动事件
        // self.edit_handler.removeInputAction(LEFT_MOUSE_UP);//取消鼠标抬起事件
      }
    }, LEFT_MOUSE_DOWN)
  }

  /**
   * 切换到绘图模式
   */
  switchToDraw() {
    //取消编辑事件
    this.isEditable = false
    //清空临时点位数据
    this.clearTempPoints()
    //清空临时选中对象
    this.resetSelectedEntity()
    //取消事件
    this.edit_handler && this.edit_handler.removeInputAction(LEFT_MOUSE_CLICK) //取消左键单击事件
    this.edit_handler && this.edit_handler.removeInputAction(LEFT_MOUSE_DOWN) //取消左键按下事件
    this.edit_handler && this.edit_handler.removeInputAction(MOUSE_MOVE) //取消鼠标移动事件
    this.edit_handler && this.edit_handler.removeInputAction(LEFT_MOUSE_UP) //取消鼠标抬起事件
  }

  //methods
  /**
   * 添加图形到Cesium.Viewer三维场景中
   */
  addGraphicToViewer(graphic, type) {
    const entity = this.viewer.entities.add(graphic)
    Cesium.defined(type) ? this.entityCollector[type].add(graphic) : ''
    return entity
  }

  //获取所有已经绘制的图形
  getGraphics() {
    return this.entityCollector
  }

  /**
   * 根据type类型获取已经绘制的图形
   */
  getGraphicsByType(type) {
    return Cesium.defined(type) ? this.entityCollector[type] : ''
  }

  /**
   * 创建Point点对象
   */
  createPoint(options = {}) {
    const {
      pixelSize = 5,
      color = Cesium.Color.RED,
      //默认位置属性
      position = new Cesium.CallbackProperty(function () {
        return undefined
      }, false), //PositionProperty|Cartesian3
    } = options
    const point = new Point({ pixelSize, color, position }) //创建点对象
    return point
  }

  /**
   * 绘制Label标签
   */
  createLabel(options = {}) {
    const {
      font = '24px sans-serif',
      fillColor = Cesium.Color.RED,
      //默认文本属性
      text = new Cesium.CallbackProperty(function () {
        return ''
      }, false),
      //默认位置属性
      position = new Cesium.CallbackProperty(function () {
        return undefined
      }, false), //PositionProperty|Cartesian3
      showBackground = true,
      backgroundColor = new Cesium.Color(0.165, 0.165, 0.165, 0.8),
    } = options
    const label = new Label({
      font,
      fillColor,
      text,
      position,
      showBackground,
      backgroundColor,
    }) //创建label标签对象
    return label
  }

  /**
   * 创建Polyline
   */
  createPolyline(options = {}) {
    const {
      positions = new Cesium.CallbackProperty(function () {
        return null
      }, false),
      width = 1.0,
      material = Cesium.Color.WHITE,
      clampToGround = true, //贴地线
      classificationType = Cesium.ClassificationType.BOTH,
    } = options
    return new Polyline({
      positions,
      width,
      material,
      clampToGround,
      classificationType,
    })
  }

  /**
   * 坐标转换工具-[窗口坐标->世界坐标]
   */
  cartesian2ToCartesian3(cartesian2) {
    //获取ray射线对象
    const ray = this.viewer.camera.getPickRay(cartesian2)
    //窗口坐标转世界坐标
    const cartesian3 = this.viewer.scene.globe.pick(ray, this.viewer.scene)
    return cartesian3
  }

  /**
   * 鼠标右键、左键事件监听
   * @param {*} leftClickCallBack 鼠标左键单击事件回调函数
   * @param {*} rightClickCallBack 鼠标右键单击事件回调函数
   */
  monitorClickEvent(
    leftClickCallBack = (e) => {
      e
    },
    rightClickCallBack = (e) => {
      e
    }
  ) {
    const self = this
    //切换到绘图模式
    this.switchToDraw()
    //创建事件处理句柄
    this.leftClick_handler = new Cesium.ScreenSpaceEventHandler(
      this.viewer.canvas
    )
    this.rightClick_handler = new Cesium.ScreenSpaceEventHandler(
      this.viewer.canvas
    )
    //注册左键单击事件
    this.leftClick_handler.setInputAction(function (event) {
      const cartesian3_Pos = self.cartesian2ToCartesian3(event.position) //窗口坐标转换为世界坐标
      typeof leftClickCallBack === 'function'
        ? leftClickCallBack(cartesian3_Pos)
        : ''
    }, LEFT_MOUSE_CLICK)
    //注册右键单击事件
    this.rightClick_handler.setInputAction(function (event) {
      //[注销左键单击事件]
      self.leftClick_handler.removeInputAction(LEFT_MOUSE_CLICK)
      self.rightClick_handler.removeInputAction(RIGHT_MOUSE_CLICK)
      typeof rightClickCallBack === 'function' ? rightClickCallBack(event) : ''
    }, RIGHT_MOUSE_CLICK)
  }

  /**
   * 绘制Point
   */
  drawPoint(pixelSize = 5, color = Cesium.Color.RED) {
    //判断是否处于编辑模式
    // if (this.isEditable) {
    //   window.alert('当前正处于编辑模式,禁止绘制操作!')
    //   return false
    // }
    const self = this
    this.monitorClickEvent(
      (cartesian3) => {
        console.warn('监听事件')
        const point = self.createPoint({
          color: color,
          pixelSize: pixelSize,
          position: new Cesium.CallbackProperty(function () {
            return cartesian3
          }, false),
        }) //创建点位
        self.addGraphicToViewer(point, 'point') //添加坐标点到场景中
      },
      () => {
        console.warn('注销事件')
      }
    )
  }

  /**
   * 绘制label
   */
  drawLabel(
    text,
    fillColor = Cesium.Color.RED,
    showBackground = true,
    backgroundColor = Cesium.Color.WHITE.withAlpha(0.5)
  ) {
    //判断是否处于编辑模式
    // if (this.isEditable) {
    //   window.alert('当前正处于编辑模式,禁止绘制操作!')
    //   return false
    // }
    const self = this
    this.monitorClickEvent(
      (cartesian3) => {
        console.warn('监听事件')
        const label = self.createLabel({
          fillColor: fillColor,
          text: text,
          position: new Cesium.CallbackProperty(function () {
            return cartesian3
          }, false),
          showBackground: showBackground,
          backgroundColor: backgroundColor,
        }) //创建点位
        self.addGraphicToViewer(label, 'label') //添加坐标点到场景中
      },
      () => {
        console.warn('注销事件')
      }
    )
  }

  /**
   * 绘制polyline线
   */
  drawPolyline() {
    //判断是否处于编辑模式
    // if (this.isEditable) {
    //   window.alert('当前正处于编辑模式,禁止绘制操作!')
    //   return false
    // }
    const self = this
    const polyline = new Polyline({
      width: 4.0,
      material: Cesium.Color.SKYBLUE,
    })
    self.addGraphicToViewer(polyline, 'polyline') //添加坐标点到场景中
    //监听鼠标事件
    this.monitorClickEvent(
      (cartesian3) => {
        console.warn('监听事件')
        //更新坐标
        polyline.updatePositions(cartesian3)
      },
      () => {
        console.warn('注销事件')
      }
    )
  }
  /**
   * 绘制Polygon
   */
  drawPolygon() {
    //判断是否处于编辑模式
    // if (this.isEditable) {
    //   window.alert('当前正处于编辑模式,禁止绘制操作!')
    //   return false
    // }
    //创建polygon
    const polygon = new Polygon({
      material: Cesium.Color.BLUE.withAlpha(0.5),
    })
    this.addGraphicToViewer(polygon, 'polygon')
    //事件监听
    this.monitorClickEvent(
      (cartesian3) => {
        console.warn('监听事件')
        //更新坐标
        polygon.updatePositions(cartesian3)
      },
      () => {
        console.warn('注销事件')
      }
    )
  }

  /**
   * 绘制Rectangle矩形-事件封装不同
   */
  drawRectangle() {
    //判断是否处于编辑模式
    // if (this.isEditable) {
    //   window.alert('当前正处于编辑模式,禁止绘制操作!')
    //   return false
    // }
    //切换到绘图模式
    this.switchToDraw()
    const self = this
    //创建矩形
    const rectangle = new Rectangle({
      material: Cesium.Color.GREEN.withAlpha(0.5),
    })
    const entity = this.addGraphicToViewer(rectangle, 'rectangle')
    let clickCount = 0 //点击次数记录
    //事件回调
    this.leftClick_handler = new Cesium.ScreenSpaceEventHandler(
      this.viewer.canvas
    )
    this.rightClick_handler = new Cesium.ScreenSpaceEventHandler(
      this.viewer.canvas
    )
    //事件注册
    this.leftClick_handler.setInputAction(function (event) {
      const cartesian3 = self.cartesian2ToCartesian3(event.position) //坐标转换
      //更新坐标点
      rectangle.updateRectangleByPositions(cartesian3, entity)
      //第一次点击
      if (clickCount === 0) {
        //注册鼠标移动事件
        self.rightClick_handler.setInputAction(function (event) {
          const movePosition = self.cartesian2ToCartesian3(event.endPosition) //记录鼠标移动位置
          //更新坐标点
          rectangle.updateRectangleByPositions(movePosition, entity)
        }, MOUSE_MOVE)
      }
      //第二次点击
      if (clickCount === 1) {
        //注销事件
        self.leftClick_handler.removeInputAction(LEFT_MOUSE_CLICK)
        self.rightClick_handler.removeInputAction(MOUSE_MOVE)
      }
      clickCount += 1 //点击次数自增
    }, LEFT_MOUSE_CLICK)
  }
}
